package atomicPlayground;

import integration.*;
import processing.core.*;
import elec.*;
import userInterface.*;
import textDisplay.*;
import java.util.*;

@SuppressWarnings("serial")
public class AtomicPlaygroundApplet extends PApplet {
	float graphicsScale = 10f;
	float[] viewTranslate = {0f, 0f};
	int stepsPerFrame = 10;
	float timeStep = 2f;
		
	AtomicParticleSystem sys;
	Integrator integ;
	
	Clickable grabbed = null;

	PFont font12, font18, font24, font30, font36, font48, font60;
	PFont[] font;

	ArrayList<ScreenText> textList;
	
	ScreenText helpMessage;
	
	public void setupSystem(int option) {
		sys = new AtomicParticleSystem(this, 1f, 0.6f);
		ElectroFlake.electricDimension = 2;
		ElectroLump.electricDimension = 2;
		integ = new integration.RungeKutta4(sys);
		
		graphicsScale = 10f;
		viewTranslate[0] = 0f; viewTranslate[1] = 0f;
		stepsPerFrame = 10;
		timeStep = 2f;

		println(option);

		textList = new ArrayList<ScreenText>();
		
		ScreenText welcomeMessage = new ScreenText(this,
		"Welcome to Electric Playground\n\n" +
		"Press space bar for help", font24, 24, 
		color(250, 250, 250), 300, width/2, height/2, CENTER, CENTER);
		
		if (option==0) {
		}
		
		if (option==1) {
			addProtonLump(40, 0, 0, true);
			welcomeMessage = new ScreenText(this,
			"Single Object", font36, 36, 
			color(250, 250, 250), 30, width/2, 3*height/4, CENTER, CENTER);
		}
		
		else if (option==2) {
			graphicsScale = 8f;
			addProtonLump(40, 30, 0, true);
			addProtonLump(40, -30, 0, true);
			for (int i = 0; i<2;++i) {
				addElectron(-30, 0);
				sys.removeElectron(30, 0);
			}
			welcomeMessage = new ScreenText(this,
					"Conduction", font36, 36, 
					color(250, 250, 250), 30, width/2, 3*height/4, CENTER, CENTER);
		}

		else if (option==3) {
			addProtonLump(40, -20, 0, true);
			addProtonLump(-10, 25, 0, false);
			welcomeMessage = new ScreenText(this,
					"Induction", font36, 36, 
					color(250, 250, 250), 30, width/2, 3*height/4, CENTER, CENTER);
		}
		
		else if (option==4) {
			graphicsScale = 5f;
			timeStep = 1f;
			addProtonLump(25, -10, 0, true);
			for (int i = -44; i <= -16; i += 2) {
				addProtonLump(1, i, 0, true);
			}
			addProtonLump(25, -50, 0, true);
			addProtonLump(-10, 50, 0, false);
			welcomeMessage = new ScreenText(this,
					"Induction", font36, 36, 
					color(250, 250, 250), 30, width/2, 3*height/4, CENTER, CENTER);
		}
		
		assignFonts();
		textList.add(welcomeMessage);
		integ.findSlope();	
	}
	
	public void setFonts() {
		font12 = loadFont("Arial-BoldMT-12.vlw");
		font18 = loadFont("Arial-BoldMT-18.vlw");
		font24 = loadFont("Arial-BoldMT-24.vlw");
		font30 = loadFont("Arial-BoldMT-30.vlw");
		font36 = loadFont("Arial-BoldMT-36.vlw");
		font48 = loadFont("Arial-BoldMT-48.vlw");
		font60 = loadFont("Arial-BoldMT-60.vlw");
		font = new PFont[50];
	}
	
	public void assignFonts() {
		for (int i = 0; i<font.length;++i) {
			float f = graphicsScale*sqrt(i);
			if (f<=12) font[i] = font12;
			else if (f<=18) font[i] = font18;
			else if (f<=24) font[i] = font24;
			else if (f<=30) font[i] = font30;
			else if (f<=36) font[i] = font36;
			else if (f<=48) font[i] = font48;
			else font[i] = font60;
		}
		ProtonLump.smallFont = font24;
	}
	
	public void setup() {
		size(600, 600);
		smooth();
		setFonts();
		textFont(font24, 24);

		setupSystem(0);
		helpMessage = new ScreenText(this,
				"[Ctrl][LMB]: Create positively charged object" +
				"\n[Ctrl][RMB]: Create negatively charged object" +
				"\n[Ctrl][L/RMB]: Increase/decrease object charge" +
				"\n[LMB] + drag: Move object" +
				"\n" +
				"\n[Shift][LMB]: Add electron" +
				"\n[Shift][RMB]: Remove electron" +
				"\n[Alt][LMB]: Neutralize object charge" +
				"\n" +
				"\n[MouseWheel]: Zoom in/out" +
				"\n[LMB] + drag: Pan view" +
				"\n[C]: Toggle charge display\n[N]: Toggle net charge display" +
				"\n[-]: Slow down simulation\n[+]: Speed up simulation" +
				"\n\n[SPACE]: Summon/dismiss help text", font18, 18, 
				color(250, 250, 250), -1, width/2, height/2, CENTER, CENTER);
		
		addMouseWheelListener(new java.awt.event.MouseWheelListener() { 
		    public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { 
		      mouseWheel(evt.getWheelRotation());
		  }}); 
		
	}

	public float cMouseX() {
		return (mouseX - width/2)/graphicsScale - viewTranslate[0];
	}
	public float cMouseY() {
		return (height/2 - mouseY)/graphicsScale - viewTranslate[1];
	}
	public float pCMouseX() {
		return (pmouseX - width/2)/graphicsScale - viewTranslate[0];
	}
	public float pCMouseY() {
		return (height/2 - pmouseY)/graphicsScale - viewTranslate[1];
	}
	
	public void addProtonLump(int c, float x, float y, boolean equalize) {
		ProtonLump p = new ProtonLump(this, c, x, y);
		sys.addProtonLump(p);
		if (equalize) {
			for (int i = 0; i < c; ++i) {
				sys.addElectron(x, y);
			}
		}
		integ.adjustSpace();
		integ.findSlope();		
	}

	public void addElectron(float x, float y) {
		Electron e = new Electron(this, x, y);
		sys.addElectron(e);
		integ.adjustSpace();
		integ.findSlope();
	}
	
	public void mouseClicked() {
		float x = cMouseX();
		float y = cMouseY();
		Clickable clicked = sys.mouseClickedIn(x, y);
		if (clicked==null) {
			if ((keyPressed==true && key==CODED && keyCode==SHIFT)){
				if (mouseButton==LEFT) {
					addElectron(x, y);
				}
				else if (mouseButton==RIGHT) {
					sys.removeElectron(x, y);
					integ.findSlope();
				}
			}
			else if (keyPressed==true && key==CODED && keyCode==CONTROL) {
				if (mouseButton==LEFT) {
					addProtonLump(1, x, y, false);
				}
				if (mouseButton == RIGHT) {
					addProtonLump(-1, x, y, false);
				}				
			}
			else if (keyPressed==true && key==CODED && keyCode==ALT) {
				if (mouseButton==LEFT) {
					for (ProtonLump p : sys.protonLumpList) {
						if (p.containsPoint(x, y)) {
							if (p.netCharge > 0) {
								for (int i=0 ; i < p.netCharge; ++i) {
									sys.addElectron(p.position()[0], p.position()[1]);
								}
								integ.adjustSpace();
								integ.findSlope();
							}
						}
					}														
				}
			}
		}
		else {integ.findSlope();}
		
		if (grabbed!=null) grabbed.mouseReleased();
		
	}
	
	public void mousePressed() {
		grabbed = sys.mousePressedIn(cMouseX(), cMouseY());
	}
	
	public void mouseReleased() {
		if (grabbed != null) grabbed.mouseReleased();
		grabbed = null;
	}
	
	public void mouseDragged() {
		float dx = cMouseX() - pCMouseX();
		float dy = cMouseY() - pCMouseY();
		if (grabbed != null) {
			grabbed.mouseDragged(dx, dy);
			sys.itemDragged(grabbed, dx, dy);
		}
		else {
			if (mouseButton==LEFT  && keyPressed == false) {
				viewTranslate[0] += dx;
				viewTranslate[1] += dy;
			}
		}
	}
	
	public void mouseWheel(int delta) {
		if (delta>0) graphicsScale *= 0.8;
		else if (delta<0) graphicsScale *= 1.25;
		assignFonts();
	}
	
	public void keyTyped() {
		if (key==' ') {
			if (textList.contains(helpMessage)) {
				helpMessage.framesToGo = (int) frameRate/2;
			}
			else {
				helpMessage.framesToGo = -1;
				textList.clear();
				textList.add(helpMessage);
			}
		}
		if (key=='C' || key=='c') {
			sys.showCharges = !sys.showCharges;
		}
		if (key=='N' || key=='n') {
			sys.showNetCharges = !sys.showNetCharges;
		}
		if (key=='-' || key=='_') {timeStep *= 0.8;}
		if (key=='=' || key=='+') {timeStep *=1.25;}
		if (key>='0' && key<='9') setupSystem(key - '0');
	}
	
	public void advance(float dt) {
		integ.finalizeSlope(dt);
		integ.takeFinalStep(dt);
		integ.findSlope();
	}
	
	public void draw() {		
		for (int i=0;i<stepsPerFrame;++i) 
			advance(timeStep/stepsPerFrame);
		
		for (ProtonLump p : sys.protonLumpList) {
			p.font = font[min((int) abs(p.charge), font.length)];
		}
		background(0, 0, 0);
		translate(width/2, height/2);
		scale(graphicsScale, -graphicsScale);
		translate(viewTranslate[0], viewTranslate[1]);
		
		sys.draw();
		
		if (textList.size()>0) for (ScreenText s: textList) s.draw();
		for (int i = textList.size()-1; i >=0 ; --i) {
			if (textList.get(i).framesToGo==0) {
				textList.remove(i);
			}
		}
	}
	
}
